package com.atguigu.tingshu.user.service.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import com.alibaba.fastjson.JSON;
import com.atguigu.tingshu.account.client.UserAccountFeignClient;
import com.atguigu.tingshu.common.constant.KafkaConstant;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.common.service.KafkaService;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.user.UserInfo;
import com.atguigu.tingshu.model.user.UserPaidAlbum;
import com.atguigu.tingshu.model.user.UserPaidTrack;
import com.atguigu.tingshu.user.context.SaveUserPaidRecordStrategyContext;
import com.atguigu.tingshu.user.mapper.UserInfoMapper;
import com.atguigu.tingshu.user.mapper.UserPaidAlbumMapper;
import com.atguigu.tingshu.user.mapper.UserPaidTrackMapper;
import com.atguigu.tingshu.user.service.UserInfoService;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.seata.spring.annotation.GlobalTransactional;
import io.swagger.v3.oas.models.security.SecurityScheme;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {

	/**
	 * aop核心内容回顾：
	 * 1、什么是aop？aop简称成面向切面编程，本质上是一种编程思想，使用这种编程思想可以在不更改源代码的情况下对类中的方法进行功能增强。
	 * 2、aop的底层的实现原理? 使用动态代理技术实现
	 * 3、常见的动态代理的技术都有哪些以及它们区别？JDK的动态代理【要求被代理的类实现接口】和CGLIB的动态代理【不要求被代理的类实现接口】
	 * 4、aop开发的常见概念？
	 * 	 目标类:  被代理的类
	 * 	 连接点： 被代理类的所有方法
	 * 	 切入点:  被代理类中要被增强的方法
	 * 	 		 定义切入点的方式：1、通过自定义注解   2、使用切入点表达式
	 * 	 通知:    增强的功能
	 * 	 		  按照增强的功能和业务功能执行的顺序进行划分，可以将通知分为如下几种：	1、前置通知   2、返回后通知【业务功能正常执行完毕以后再执行增强的功能】   3、后置(最终)通知(业务功能完毕以后再执行增强的功能)
	 * 	 		  																4、异常通知(业务代码执行的时候产生了异常，那么此时就会执行该通知代码)
	 * 	 		  																5、环绕通知
	 * 	 切面: 通知 + 切入点
	 */

	@Autowired
	private UserInfoMapper userInfoMapper;

	@Autowired
	private WxMaService wxMaService ;

	@Autowired
	private RedisTemplate<String , String> redisTemplate ;

	@Autowired
	private KafkaService kafkaService ;

	@Autowired
	private UserPaidAlbumMapper userPaidAlbumMapper ;

	@Autowired
	private UserPaidTrackMapper userPaidTrackMapper ;

	@Autowired
	private UserAccountFeignClient userAccountFeignClient ;

	@GlobalTransactional            // 在业务入口方法上添加@GlobalTransactional注解
	@Override
	public String login(String code) {

		try {

			// 1、请求微服服务器登录凭证校验接口校验临时凭证的合法性
			// 2、如果校验没有抛出异常，就说明临时凭证合法，用户登录成功了，微信服务端会返回openId
			// 3、如果校验抛出了一场，就说明临时凭证不合法，用户登录失败了
			WxMaJscode2SessionResult sessionResult = wxMaService.getUserService().getSessionInfo(code);
			String openid = sessionResult.getOpenid();

			// 4、使用openId从user_info表中查询用户数据
			LambdaQueryWrapper<UserInfo> lambdaQueryWrapper = new LambdaQueryWrapper<>() ;
			lambdaQueryWrapper.eq(UserInfo::getWxOpenId,openid) ;
			UserInfo userInfo = userInfoMapper.selectOne(lambdaQueryWrapper);

			// 5、如果不可以查询到用户数据，则表示用户第一次登录，需要注册用户(向user_info表中插入一条数据)
			if(userInfo == null) {

				// 创建一个UserInfo对象
				userInfo = new UserInfo() ;
				userInfo.setWxOpenId(openid);
				userInfo.setNickname("硅谷听友" + System.currentTimeMillis());
				userInfo.setAvatarUrl("https://oss.aliyuncs.com/aliyun_id_photo_bucket/default_handsome.jpg");
				userInfoMapper.insert(userInfo) ;

				// 6、如果是新注册的用户，此时就需要初始化用户的账号信息(向kafka中发送一条初始化用户账号信息的消息) ，service-account微服务需要从kafka中获取消息，初始化用户的账号数据
				// kafkaService.sendMsg(KafkaConstant.QUEUE_USER_REGISTER , String.valueOf(userInfo.getId()));
				userAccountFeignClient.initAccount(userInfo.getId()) ;

				// 模拟异常
				int a = 1 / 0 ;

			}

			// 7、保存用户的登录状态：生成token，将token保存到redis中，同时将token返回给前端
			String token = UUID.randomUUID().toString().replace("-", "");
			String userLoginRedisKey = RedisConstant.USER_LOGIN_KEY_PREFIX + token ;
			redisTemplate.opsForValue().set(userLoginRedisKey , JSON.toJSONString(userInfo) , RedisConstant.USER_LOGIN_KEY_TIMEOUT , TimeUnit.DAYS);

			// 返回
			return token ;

		}catch (Exception e) {
			e.printStackTrace();
			throw  new GuiguException(ResultCodeEnum.DATA_ERROR);
		}

	}

	@Override
	public UserInfo getUserInfo() {
		Long userId = AuthContextHolder.getUserId();					// 获取当前登录用户id
		UserInfo userInfo = userInfoMapper.selectById(userId);
		return userInfo;
	}

	@Override
	public void updateUser(UserInfo userInfo) {

		// 修改用户信息
		Long userId = AuthContextHolder.getUserId();
		UserInfo dbUserInfo = userInfoMapper.selectById(userId);
		dbUserInfo.setAvatarUrl(userInfo.getAvatarUrl());
		dbUserInfo.setNickname(userInfo.getNickname());
		userInfoMapper.updateById(dbUserInfo);

	}

	@Override
	public Map<Long, Integer> userIsPaidTrack(Long albumId, List<Long> trackIds) {

		// 创建一个Map对象，封装结果数据
		Map<Long , Integer> map = new HashMap<>() ;

		// 判断用户是否购买过专辑
		Long userId = AuthContextHolder.getUserId();
		LambdaQueryWrapper<UserPaidAlbum> lambdaQueryWrapper = new LambdaQueryWrapper<>() ;
		lambdaQueryWrapper.eq(UserPaidAlbum::getUserId,userId) ;
		lambdaQueryWrapper.eq(UserPaidAlbum::getAlbumId,albumId) ;
		UserPaidAlbum userPaidAlbum = userPaidAlbumMapper.selectOne(lambdaQueryWrapper);

		if(userPaidAlbum != null) {		// 用户购买过专辑
			trackIds.forEach(trackId -> {
				map.put(trackId , 1) ;
			});
		}else {  // 用户未购买过专辑

			// 判断传递过来的声音用户是否购买过： select * from user_paid_track where user_id = ? and album_id = ? and track_id in (? , ? , ?)
			LambdaQueryWrapper<UserPaidTrack> userPaidTrackLambdaQueryWrapper = new LambdaQueryWrapper<>() ;
			userPaidTrackLambdaQueryWrapper.eq(UserPaidTrack::getUserId,userId) ;
			userPaidTrackLambdaQueryWrapper.eq(UserPaidTrack::getAlbumId , albumId) ;
			userPaidTrackLambdaQueryWrapper.in(UserPaidTrack::getTrackId , trackIds) ;
			List<UserPaidTrack> userPaidTrackList = userPaidTrackMapper.selectList(userPaidTrackLambdaQueryWrapper);

			if(CollectionUtils.isEmpty(userPaidTrackList)) {
				trackIds.forEach(trackId -> {
					map.put(trackId , 0) ;
				});
			}else {

				List<Long> paidTrackIds = userPaidTrackList.stream().map(userPaidTrack -> userPaidTrack.getTrackId()).collect(Collectors.toList());
				trackIds.forEach(trackId -> {
					if(paidTrackIds.contains(trackId)) {
						map.put(trackId , 1) ;
					}else {
						map.put(trackId , 0) ;
					}
				});

			}

		}

		return map;
	}

	@Override
	public Boolean isPaidAlbum(Long albumId) {

		// 根据当前登录用户id和albumId查询用户是否购买过这个专辑
		Long userId = AuthContextHolder.getUserId();
		LambdaQueryWrapper<UserPaidAlbum> lambdaQueryWrapper = new LambdaQueryWrapper<>() ;
		lambdaQueryWrapper.eq(UserPaidAlbum::getUserId , userId) ;
		lambdaQueryWrapper.eq(UserPaidAlbum::getAlbumId , albumId) ;
		Long count = userPaidAlbumMapper.selectCount(lambdaQueryWrapper);

		return count > 0 ;
	}

	@Override
	public List<Long> findUserPaidTrackList(Long albumId) {

		// 根据当前登录用户的id和专辑id查询用户购买的声音id列表
		Long userId = AuthContextHolder.getUserId();
		LambdaQueryWrapper<UserPaidTrack> lambdaQueryWrapper = new LambdaQueryWrapper<>() ;
		lambdaQueryWrapper.eq(UserPaidTrack::getUserId , userId) ;
		lambdaQueryWrapper.eq(UserPaidTrack::getAlbumId , albumId) ;
		List<UserPaidTrack> userPaidTrackList = userPaidTrackMapper.selectList(lambdaQueryWrapper);
		List<Long> ids = userPaidTrackList.stream().map(userPaidTrack -> userPaidTrack.getTrackId()).collect(Collectors.toList());

		return ids;
	}

	@Override
	public void saveUserPaidRecord(UserPaidRecordVo userPaidRecordVo) {

		// 根据付款项目类型的不同，执行不同的保存购买信息的逻辑代码
		String itemType = userPaidRecordVo.getItemType();

		// 使用策略设计模式进行完成
		SaveUserPaidRecordStrategyContext.getSaveUserPaidRecordStrategy(itemType).saveUserPaidRecord(userPaidRecordVo);

	}

	@Override
	public void updateVipExpireStatus() {

		// 查询那些过期vip用户，然后将其isVip的字段值改为0
		// select * from user_info where is_vip = 1 and vip_expire_time < ?
		LambdaQueryWrapper<UserInfo> lambdaQueryWrapper = new LambdaQueryWrapper<>() ;
		lambdaQueryWrapper.eq(UserInfo::getIsVip, 1 );
		lambdaQueryWrapper.lt(UserInfo::getVipExpireTime, new Date()) ;
		List<UserInfo> userInfoList = userInfoMapper.selectList(lambdaQueryWrapper);
		userInfoList.stream().forEach(userInfo -> {
			userInfo.setIsVip(0);
			userInfoMapper.updateById(userInfo) ;
		});

	}

}
